// @author A0101200R

package storage;

import java.util.ArrayList;

import applogger.AppLogger;

public class DetectCollision {

	// list of messages
	private static final String MSG_EMPTY_STRING = "";
	private static final String MSG_NO_COLLISION = "No collision.";
	private static final String MSG_EXIST_SAME_TASKNAME = "A task with the same task name exists already.";
	private static final String MSG_TASK_ADDED = "The task is already added";

	// list of error messages
	private static final String ERROR_COLLISION = "There is a time collision with another task.";

	// list of variables from another class
	ArrayList<ScheduleEvent> eventsList;
	ScheduleEvent eventToBeAdded;

	// list of variables
	boolean isDateEqual = false;
	boolean isStartTimeEqual = false;
	boolean isEndTimeEqual = false;

	// constructor
	public DetectCollision(ArrayList<ScheduleEvent> eventsList,
			ScheduleEvent eventToBeAdded) {
		this.eventsList = eventsList;
		this.eventToBeAdded = eventToBeAdded;
	}

	/**
	 * Check if 2 events have same names. If they have the same names and all
	 * the details, we will remind the task is already added. If they have same
	 * names but different details, we will remind there is 2 tasks with same
	 * name. 
	 * 
	 * @return MSG_TASK_ADDED or MSG_EXIST_SAME_TASKNAME or MSG_EMPTY_STRING
	 */
	public String checkNameCollision() {
		for (int i = 0; i < eventsList.size(); i++) {
			ScheduleEvent otherEvent = eventsList.get(i);
			
			String output = checkNameOfTwoTasks(otherEvent);
			if (!output.equals(MSG_EMPTY_STRING)) {
				return output;
			}		
		}
		return MSG_EMPTY_STRING;
	}
	
	/**
	 * Check if the 2 tasks of the same name with different details collide 
	 * or if the 2 tasks of the same name with same details collide.
	 *  
	 * @param otherEvent
	 * @return message
	 */
	protected String checkNameOfTwoTasks(ScheduleEvent otherEvent) {
		String message = MSG_EMPTY_STRING;
		
		if (eventToBeAdded.getName().equals(otherEvent.getName())) { 
			
			boolean hasDate = eventToBeAdded.getDate() != null;
			boolean hasStartTime = eventToBeAdded.getStartTime() != null;
			boolean hasEndTime = eventToBeAdded.getEndTime() != null;

			boolean otherHasDate = otherEvent.getDate() != null;
			boolean otherHasStartTime = otherEvent.getStartTime() != null;
			boolean otherHasEndTime = otherEvent.getEndTime() != null;

			if (hasDate && otherHasDate) {
				isDateEqual = eventToBeAdded.getDate().equals(
						otherEvent.getDate());
			}
			if (hasStartTime && otherHasStartTime) {
				isStartTimeEqual = eventToBeAdded.getStartTime().equals(
						otherEvent.getStartTime());
			}
			if (hasEndTime && otherHasEndTime) {
				isEndTimeEqual = eventToBeAdded.getEndTime().equals(
						otherEvent.getEndTime());
			}

			if (isDateEqual && isStartTimeEqual && isEndTimeEqual) {
				message =  MSG_TASK_ADDED;
				
			} else {
				message = MSG_EXIST_SAME_TASKNAME;
			}
		}
		return message;
	}

	/**
	 * Check if 2 events have different dates, means there is no collision
	 * between the tasks. If they have the same dates, check on the 2 events
	 * start and end time to see if they have any collisions. 
	 * 
	 * @return MSG_NO_COLLISION or ERROR_COLLISION
	 */
	public String checkTimeCollision() {
		for (int i = 0; i < eventsList.size(); i++) {
			ScheduleEvent otherEvent = eventsList.get(i);
		
			if (!eventToBeAdded.getDate().equals(otherEvent.getDate())) {
				return MSG_NO_COLLISION;
			}

			boolean hasStartTime = eventToBeAdded.getStartTime() != null;
			boolean hasEndTime = eventToBeAdded.getEndTime() != null;
			boolean hasStartEndTime = hasStartTime && hasEndTime;

			boolean otherHasStartTime = otherEvent.getStartTime() != null;
			boolean otherHasEndTime = otherEvent.getEndTime() != null;
			boolean otherHasStartEndTime = otherHasStartTime && otherHasEndTime;

			int eventStartTime = 0, eventEndTime = 0, otherStartTime = 0, otherEndTime = 0;

			try {
				if (hasStartTime) {
					eventStartTime = Integer.parseInt(eventToBeAdded.getStartTime());
				}

				if (hasEndTime) {
					eventEndTime = Integer.parseInt(eventToBeAdded.getEndTime());
				}

				if (otherHasStartTime) {
					otherStartTime = Integer.parseInt(otherEvent.getStartTime());
				}

				if (otherHasEndTime) {
					otherEndTime = Integer.parseInt(otherEvent.getEndTime());
				}
				
			} catch (Exception e) {
				AppLogger.log("Error while converting times to integer in DetectCollision");
			}

			boolean hasCollision = false;
			
			hasCollision = checkSingleTimedTask(hasStartTime, hasEndTime,
					otherHasStartEndTime, eventStartTime, eventEndTime,
					otherStartTime, otherEndTime, hasCollision);
			
			hasCollision = checkTwoTimedTask(hasStartEndTime,
					otherHasStartEndTime, eventStartTime, eventEndTime,
					otherStartTime, otherEndTime, hasCollision);
			
			if (hasCollision) {
				return ERROR_COLLISION;
			}

		}

		return MSG_NO_COLLISION;
	}
	
	/**
	 * Check if the task to be added has a start or end time that 
	 * clashes with the duration of another task.
	 * 
	 * @param hasStartTime
	 * @param hasEndTime
	 * @param otherHasStartEndTime
	 * @param eventStartTime
	 * @param eventEndTime
	 * @param otherStartTime
	 * @param otherEndTime
	 * @param hasCollision
	 * @return true if the tasks collide.
	 */
	protected boolean checkSingleTimedTask(boolean hasStartTime,
			boolean hasEndTime, boolean otherHasStartEndTime,
			int eventStartTime, int eventEndTime, int otherStartTime,
			int otherEndTime, boolean hasCollision) {
		
		if (hasStartTime) { 
			if (otherHasStartEndTime && (otherStartTime <= eventStartTime)
					&& (otherEndTime >= eventStartTime)) {
				hasCollision = true;
			}
		}

		if (hasEndTime) { 
			if (otherHasStartEndTime && (otherStartTime < eventEndTime)
					&& (otherEndTime >= eventEndTime)) {
				hasCollision = true;
			}
		}
		
		return hasCollision;
	}

	/**
	 * Check if the two Timed ScheduleEvent's duration clash.
	 * 
	 * @param hasStartEndTime
	 * @param otherHasStartEndTime
	 * @param eventStartTime
	 * @param eventEndTime
	 * @param otherStartTime
	 * @param otherEndTime
	 * @param hasCollision
	 * @return true if the tasks collide.
	 */
	protected boolean checkTwoTimedTask(boolean hasStartEndTime,
			boolean otherHasStartEndTime, int eventStartTime, int eventEndTime,
			int otherStartTime, int otherEndTime, boolean hasCollision) {
		
		if (hasStartEndTime && otherHasStartEndTime) { 
			
			if ((eventStartTime <= otherStartTime)
					&& (eventEndTime >= otherEndTime)) { 
				hasCollision = true;

			} else if ((eventStartTime >= otherStartTime)
					&& (eventEndTime <= otherEndTime)) { 
				hasCollision = true;

			} else if ((eventStartTime >= otherStartTime)
					&& (eventStartTime <= otherEndTime)) { 
				hasCollision = true;

			} else if ((eventStartTime <= otherStartTime)
					&& (eventEndTime >= otherStartTime)) { 
				hasCollision = true;

			} else if ((eventEndTime >= otherStartTime)
					&& (eventEndTime <= otherEndTime)) { 
				hasCollision = true;

			} else if ((otherEndTime >= eventStartTime)
					&& (otherEndTime <= eventEndTime)) { 
				hasCollision = true;
			}
		}
		return hasCollision;
	}
}
